/*
 * Wazuh Vulnerability scanner - Scan Orchestrator
 * Copyright (C) 2015, Wazuh Inc.
 * March 11, 2024.
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation.
 */

#ifndef _EVENT_INSERT_INVENTORY_HPP
#define _EVENT_INSERT_INVENTORY_HPP

#include "../policyManager/policyManager.hpp"
#include "chainOfResponsability.hpp"
#include "inventorySync.hpp"
#include "scanContext.hpp"

/**
 * @brief TEventInsertInventory class.
 * This class is in charge of inserting the inventory entries for a given agent and affected component type.
 * It receives the scan context and the inventory database and returns the scan context with the inventory updated.
 * The affected component type can be Os or Package.
 * Os: Insert inventory of the OS for the agent.
 * Package: Insert a package into the inventory for the agent.
 *
 * @tparam TScanContext scan context type.
 */
template<typename TScanContext = ScanContext>
class TEventInsertInventory final
    : public AbstractHandler<std::shared_ptr<TScanContext>>
    , public TInventorySync<TScanContext>
{
public:
    // LCOV_EXCL_START
    /**
     * @brief EventInsertInventory constructor.
     *
     * @param inventoryDatabase Inventory database.
     */
    explicit TEventInsertInventory(Utils::RocksDBWrapper& inventoryDatabase)
        : TInventorySync<TScanContext>(inventoryDatabase)
    {
    }
    // LCOV_EXCL_STOP

    /**
     * @brief Handles request and passes control to the next step of the chain.
     *
     * @param data Scan context.
     * @return std::shared_ptr<ScanContext> Abstract handler.
     */
    std::shared_ptr<TScanContext> handleRequest(std::shared_ptr<TScanContext> data) override
    {
        std::string key;
        key.append(data->agentNodeName());
        key.append("_");
        key.append(data->agentId());
        key.append("_");

        // Create the key for the inventory.
        key.append(TInventorySync<TScanContext>::affectedComponentKey(data));

        std::vector<std::string> insertList;
        std::string insertListString;
        std::string value;
        const auto& column = AFFECTED_COMPONENT_COLUMNS.at(data->affectedComponentType());

        // Build json element for each CVE.
        for (auto& [cve, value] : data->m_elements)
        {
            std::string elementKey = key;
            elementKey.append("_");
            elementKey.append(cve);

            value = TInventorySync<TScanContext>::buildElement("INSERTED", elementKey);
        }

        if (TInventorySync<TScanContext>::m_inventoryDatabase.get(key, value, column))
        {
            auto listCve = Utils::split(value, ',');
            for (auto& [key, value] : data->m_elements)
            {
                if (std::find(listCve.begin(), listCve.end(), key) == listCve.end())
                {
                    // Concatenate all cves to insert into the inventory.
                    insertListString.append(key);
                    insertListString.append(",");
                }
            }

            if (!insertListString.empty())
            {
                insertListString.append(value);
                logDebug2(
                    WM_VULNSCAN_LOGTAG, "Updating agent package key: %s -> %s", key.c_str(), insertListString.c_str());
                TInventorySync<TScanContext>::m_inventoryDatabase.put(key, insertListString, column);
            }
        }
        else
        {
            for (auto& [key, value] : data->m_elements)
            {
                // Concatenate all cves to insert into the inventory.
                insertListString.append(key);
                insertListString.append(",");
            }

            if (!insertListString.empty())
            {
                insertListString.pop_back();
                logDebug2(
                    WM_VULNSCAN_LOGTAG, "Inserting agent package key: %s -> %s", key.c_str(), insertListString.c_str());
                TInventorySync<TScanContext>::m_inventoryDatabase.put(key, insertListString, column);
            }
        }

        return AbstractHandler<std::shared_ptr<TScanContext>>::handleRequest(std::move(data));
    }
};

using EventInsertInventory = TEventInsertInventory<>;

#endif // _EVENT_INSERT_INVENTORY_HPP
